﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows.Input;
using Analyzer;
using Analyzer.ViewModel;
using Model;

namespace ReportBrowser.ViewModel
{
    internal class MessageViewModel : ViewModelBase
    {
        #region Nested

        private class TreeViewMouseLeftButtonDownCommand : ICommand
        {
            private MessageViewModel mParent;

            public TreeViewMouseLeftButtonDownCommand(MessageViewModel parent)
            {
                mParent = parent;
            }

            public void Execute(object parameter)
            {
                mParent.IsSelected = !mParent.IsSelected;
                System.Windows.MessageBox.Show("IsSelected == " + mParent.IsSelected.ToString());
            }

            public bool CanExecute(object parameter)
            {
                return true;
            }

            public event EventHandler CanExecuteChanged;
        }

        #endregion Nested

        #region Fields

        private static List<MessageViewModel> sInstances = new List<MessageViewModel>();

        #endregion Fields

        #region Lifecylce

        public MessageViewModel(Message model, ActualEntities context)
        {
            this.Model = model;
            if (DesignerProperties.GetIsInDesignMode(new System.Windows.DependencyObject()) == false
                && context.Messages.Where(m => m.Id == model.Id).Count() == 0)
                throw new ArgumentException("Entitiy 'model' not found in 'context.MessagesVM'", "context");
            this.Context = context;
            RefreshIssues();
            if (DesignerProperties.GetIsInDesignMode(new System.Windows.DependencyObject()) == false)
                RefreshSubcharacteristics();
            MessageViewModel.InstanceSelected += new EventHandler(MessageViewModel_InstanceSelected);

            sInstances.Add(this);
        }

        #endregion Lifecycle

        #region Properties

        /// <summary>
        /// Gets the model of this viewmodel.
        /// </summary>
        public Message Model { get; private set; }

        private ModuleViewModel pModule;
        public ModuleViewModel Module
        {
            get
            {
                if (pModule == null && this.Model.Module != null)
                    pModule = new ModuleViewModel(this.Model.Module);
                return pModule;
            }
        }

        private NamespaceViewModel pNamespace;
        public NamespaceViewModel Namespace
        {
            get
            {
                if (pNamespace == null && this.Model.Namespace != null)
                    pNamespace = new NamespaceViewModel(this.Model.Namespace);
                return pNamespace;
            }
        }

        private TypeViewModel pType;
        public TypeViewModel Type
        {
            get
            {
                if (pType == null && this.Model.Type != null)
                    pType = new TypeViewModel(this.Model.Type, this.Context);
                return pType;
            }
        }

        private MemberViewModel pMember;
        public MemberViewModel Member
        {
            get
            {
                if (pMember == null && this.Model.Member != null)
                    pMember = new MemberViewModel(this.Model.Member, isTruncatedAsCaller: false);
                return pMember;
            }
            private set
            {
                if (value != pMember)
                {
                    pMember = value;
                    OnPropertyChanged("Member");
                }
            }
        }

        /// <summary>
        /// Gets the collection of the issues related to the message.
        /// </summary>
        public ObservableCollection<FxCopIssue> Issues { get; private set; }

        /// <summary>
        /// Gets the collection of ISO 9126 quality subcharacteristics related to the message.
        /// </summary>
        public ObservableCollection<Category_Subcharacteristic> Subcharacteristics { get; private set; }

        private ActualEntities Context { get; set; }

        private bool pIsSelected;
        public bool IsSelected
        {
            get { return pIsSelected; }
            set
            {
                if (value != pIsSelected)
                {
                    pIsSelected = value;
                    OnPropertyChanged("IsSelected");
                    //if (pIsSelected == true)
                    //    MessageViewModel.OnInstanceSelected(this);
                    //else
                    //    this.Member = null;
                }
            }
        }

        public double Weight { get { return this.Model.GetWeight(); } }

        #endregion Properties

        #region Commands

        private ICommand pSelectCommand;
        public ICommand SelectCommand
        {
            get
            {
                if (pSelectCommand == null)
                    //pSelectCommand = new TreeViewMouseLeftButtonDownCommand(this);
                    pSelectCommand = new RelayCommand(
                        p =>
                        {
                            System.Diagnostics.Debug.WriteLine("[MessageViewModel.SelectCommand] sessionKey.HashCode == " + p.GetHashCode().ToString());
                            MemberViewModel.StartNewSession(p);
                            if (pIsSelected == true)
                                MessageViewModel.OnInstanceSelected(this);
                            else
                                this.Member = null;
                        });
                return pSelectCommand;
            }
        }

        #endregion Commands

        #region Methods

        /// <summary>
        /// Finds <see cref="MessageViewModel"/> instances that has a model with the given arguments.
        /// </summary>
        /// <param name="assemblyName"></param>
        /// <param name="moduleName"></param>
        /// <param name="namespaceName"></param>
        /// <param name="typeName"></param>
        /// <param name="memberName"></param>
        /// <returns>The found instances</returns>
        internal static IEnumerable<MessageViewModel> FindByMember(string assemblyName, string moduleName,
            string namespaceName, string typeName, string memberName)
        {
            return sInstances.Where(mvm =>
                mvm.Model.AssemblyName.Equals(assemblyName)
                && mvm.Module != null && mvm.Module.Model.Name.Equals(moduleName)
                && mvm.Namespace != null && mvm.Namespace.Model.Name.Equals(namespaceName)
                && mvm.Type != null && mvm.Type.Model.Name.Equals(typeName)
                && mvm.Member != null && mvm.Member.Model.Name.Equals(memberName));
        }

        internal static bool IsAnyByMember(string assemblyName, string moduleName,
            string namespaceName, string typeName, string memberName)
        {
            return sInstances.Any(mvm =>
                mvm.Model.AssemblyName.Equals(assemblyName)
                && mvm.Module != null && mvm.Module.Model.Name.Equals(moduleName)
                && mvm.Namespace != null && mvm.Namespace.Model.Name.Equals(namespaceName)
                && mvm.Type != null && mvm.Type.Model.Name.Equals(typeName)
                && mvm.Member != null && mvm.Member.Model.Name.Equals(memberName));
        }

        /// <summary>
        /// Finds <see cref="MessageViewModel"/> instances that has a model with the given arguments.
        /// If <paramref name="isCummulative"/> is set to true, messages about mebers of the specified type
        /// will be included.
        /// </summary>
        /// <param name="assemblyName"></param>
        /// <param name="moduleName"></param>
        /// <param name="namespaceName"></param>
        /// <param name="typeName"></param>
        /// <param name="isCummulative">Determines wether the messages about mebers of the specified type
        /// will be included, or not.</param>
        /// <returns>The found instances</returns>
        internal static IEnumerable<MessageViewModel> FindByType(string assemblyName, string moduleName,
            string namespaceName, string typeName, bool isCummulative = false)
        {
            return sInstances.Where(mvm =>
                mvm.Model.AssemblyName.Equals(assemblyName)
                && mvm.Module != null && mvm.Module.Model.Name.Equals(moduleName)
                && mvm.Namespace != null && mvm.Namespace.Model.Name.Equals(namespaceName)
                && mvm.Type != null && mvm.Type.Model.Name.Equals(typeName)
                && ((isCummulative == false && mvm.Member == null)
                    || isCummulative == true));
        }

        internal static bool IsAnyByType(string assemblyName, string moduleName,
            string namespaceName, string typeName, bool isCummulative = false)
        {
            return sInstances.Any(mvm =>
                mvm.Model.AssemblyName.Equals(assemblyName)
                && mvm.Module != null && mvm.Module.Model.Name.Equals(moduleName)
                && mvm.Namespace != null && mvm.Namespace.Model.Name.Equals(namespaceName)
                && mvm.Type != null && mvm.Type.Model.Name.Equals(typeName)
                && ((isCummulative == false && mvm.Member == null)
                    || isCummulative == true));
        }

        /// <summary>
        /// Finds <see cref="MessageViewModel"/> instances that has a model with the given arguments.
        /// If <paramref name="isCummulative"/> is set to true, messages about types in the specified
        /// namespace and mebers of those types will be included.
        /// </summary>
        /// <param name="namespaceName"></param>
        /// <param name="isCummulative">Determines wether the messages about types in the specified
        /// namespace and mebers of those types will be included, or not.</param>
        /// <returns>The found instances</returns>
        internal static IEnumerable<MessageViewModel> FindByNamespace(string namespaceName,
            bool isCummulative = false)
        {
            return sInstances.Where(mvm =>
                mvm.Namespace != null && mvm.Namespace.Model.Name.Equals(namespaceName)
                && ((isCummulative == false && mvm.Type == null && mvm.Member == null)
                    || isCummulative == true));
        }

        internal static bool IsAnyByNamespace(string namespaceName,
            bool isCummulative = false)
        {
            return sInstances.Any(mvm =>
                mvm.Namespace != null && mvm.Namespace.Model.Name.Equals(namespaceName)
                && ((isCummulative == false && mvm.Type == null && mvm.Member == null)
                    || isCummulative == true));
        }

        /// <summary>
        /// Finds <see cref="MessageViewModel"/> instances that has a model with the given arguments.
        /// If <paramref name="isCummulative"/> is set to true, messages about types in the specified
        /// module and mebers of those types will be included.
        /// </summary>
        /// <param name="assemblyName"></param>
        /// <param name="moduleName"></param>
        /// <param name="isCummulative">Determines wether the messages about types in the specified
        /// module and mebers of those types will be included, or not.</param>
        /// <returns>The found instances</returns>
        internal static IEnumerable<MessageViewModel> FindByModule(string assemblyName, string moduleName,
            bool isCummulative = false)
        {
            return sInstances.Where(mvm =>
                mvm.Model.AssemblyName.Equals(assemblyName)
                && mvm.Module != null && mvm.Module.Model.Name.Equals(moduleName)
                && ((isCummulative == false && mvm.Type == null && mvm.Member == null)
                    || isCummulative == true));
        }

        internal static bool IsAnyByModule(string assemblyName, string moduleName,
            bool isCummulative = false)
        {
            return sInstances.Any(mvm =>
                mvm.Model.AssemblyName.Equals(assemblyName)
                && mvm.Module != null && mvm.Module.Model.Name.Equals(moduleName)
                && ((isCummulative == false && mvm.Type == null && mvm.Member == null)
                    || isCummulative == true));
        }

        internal static void ClearInstances()
        {
            sInstances.Clear();
        }

        private void RefreshIssues()
        {
            if (this.Issues == null)
                this.Issues = new ObservableCollection<FxCopIssue>();
            foreach (var issue in this.Model.Message_FxCopMessage.FxCopIssues)
            {
                this.Issues.Add(issue);
            }
        }

        private void RefreshSubcharacteristics()
        {
            if (this.Subcharacteristics == null)
                this.Subcharacteristics = new ObservableCollection<Category_Subcharacteristic>();
            foreach (var mapping in this.Model.Rule.Mappings)
            {
                this.Subcharacteristics.Add(mapping.Category.Category_Subcharacteristic);
            }
        }

        #endregion Methods

        #region Events

        public static event EventHandler InstanceSelected;

        private static void OnInstanceSelected(MessageViewModel instance)
        {
            if (InstanceSelected != null)
                InstanceSelected(instance, EventArgs.Empty);
        }

        #endregion Events

        #region EventHandlers

        void MessageViewModel_InstanceSelected(object sender, EventArgs e)
        {
            if (this.Equals(sender) == false)
            {
                this.IsSelected = false;
                this.Member = null;
            }
        }

        #endregion EventHandlers
    }
}
